package org.cmisarium.xcmis.fsprovider;

import org.xcmis.spi.CmisConstants;
import org.xcmis.spi.ConstraintException;
import org.xcmis.spi.ContentStream;
import org.xcmis.spi.DocumentData;
import org.xcmis.spi.FolderData;
import org.xcmis.spi.ItemsIterator;
import org.xcmis.spi.NameConstraintViolationException;
import org.xcmis.spi.ObjectData;
import org.xcmis.spi.ObjectNotFoundException;
import org.xcmis.spi.PermissionService;
import org.xcmis.spi.PolicyData;
import org.xcmis.spi.StorageException;
import org.xcmis.spi.TypeManager;
import org.xcmis.spi.UpdateConflictException;
import org.xcmis.spi.VersioningException;
import org.xcmis.spi.basic.BasicStorage;
import org.xcmis.spi.model.AccessControlEntry;
import org.xcmis.spi.model.Property;
import org.xcmis.spi.model.RepositoryInfo;
import org.xcmis.spi.model.TypeDefinition;
import org.xcmis.spi.model.UnfileObject;
import org.xcmis.spi.model.VersioningState;
import org.xcmis.spi.utils.CmisUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class StorageImpl extends BasicStorage
{

   private File rootFolder;

   public StorageImpl(File rootFolder, RepositoryInfo repositoryInfo, TypeManager typeManager,
      PermissionService permissionService)
   {
      super(repositoryInfo, typeManager, permissionService);
      this.rootFolder = rootFolder;
   }

   public ObjectData getObjectById(String objectId) throws ObjectNotFoundException
   {

      return data(PathCoder.idToPath(objectId));
   }

   public ObjectData getObjectByPath(String path) throws ObjectNotFoundException
   {

      return data(path);
   }

   public Collection<String> deleteTree(FolderData folder, boolean deleteAllVersions, UnfileObject unfileObject,
      boolean continueOnFailure) throws UpdateConflictException
   {
      File file = ((FolderDataImpl)folder).file;

      ArrayList<String> ids = new ArrayList<String>();
      traverseDelete(file, ids);
      return ids;
   }

   private void traverseDelete(File parent, ArrayList<String> ids)
   {
      if (parent.isDirectory())
      {
         File[] children = parent.listFiles();
         for (File f : children)
         {
            traverseDelete(f, ids);
         }
      }
      ids.add(PathCoder.pathToId(FolderDataImpl.path(parent.getAbsolutePath(), rootFolder.getAbsolutePath())));
      if (!parent.delete())
         throw new RuntimeException("Can not delete " + parent.getAbsolutePath());

   }

   public void deleteObject(ObjectData object, boolean deleteAllVersions) throws VersioningException,
      UpdateConflictException, StorageException
   {
      ((ObjectDataImpl)object).delete();

   }

   public FolderData createFolder(FolderData parent, TypeDefinition typeDefinition,
      Map<String, Property<?>> properties, List<AccessControlEntry> acl, Collection<PolicyData> policy)
      throws ConstraintException, NameConstraintViolationException, StorageException
   {
      return ((FolderDataImpl)parent).createFolder((String)properties.get(CmisConstants.NAME).getValues().get(0));
   }

   @Override
   public DocumentData createDocument(FolderData parent, TypeDefinition typeDefinition,
      Map<String, Property<?>> properties, ContentStream content, List<AccessControlEntry> acl,
      Collection<PolicyData> policies, VersioningState versioningState) throws ConstraintException,
      NameConstraintViolationException, IOException, StorageException
   {
      return ((FolderDataImpl)parent).createDocument((String)properties.get(CmisConstants.NAME).getValues().get(0),
         content);
   }

   protected ObjectDataImpl data(String path) throws ObjectNotFoundException
   {
      File file;

      if (rootFolder.getAbsolutePath().equals(path))
         file = rootFolder;
      else
         file = new File(rootFolder, path);

      if (!file.exists())
         throw new ObjectNotFoundException("File not found " + file.getAbsolutePath());

      return objectData(file);

   }

   protected ObjectDataImpl objectData(File file)
   {
      if (file.isDirectory())
         return new FolderDataImpl(file, rootFolder.getAbsolutePath(), typeManager);
      else
         return new DocumentDataImpl(file, rootFolder.getAbsolutePath(), typeManager);

   }

   public DocumentData copyDocument(DocumentData source, FolderData parent, Map<String, Property<?>> properties,
      List<AccessControlEntry> acl, Collection<PolicyData> policies, VersioningState versionState)
      throws ConstraintException, NameConstraintViolationException, StorageException
   {

      try
      {
         return ((FolderDataImpl)parent).createDocument((String)properties.get(CmisConstants.NAME).getValues().get(0),
            source.getContentStream());
      }
      catch (IOException e)
      {
         throw new StorageException(e);
      }
   }

   public ObjectData moveObject(ObjectData object, FolderData target, FolderData source)
      throws UpdateConflictException, VersioningException, NameConstraintViolationException, StorageException
   {
      File oldFile = ((ObjectDataImpl)object).file;
      File newFile = new File(((FolderDataImpl)target).file, oldFile.getName());
      if (!oldFile.renameTo(newFile))
         throw new StorageException("Can not rename " + oldFile.getAbsolutePath() + " to " + newFile.getAbsolutePath());

      return objectData(newFile);
   }

   public ItemsIterator<DocumentData> getCheckedOutDocuments(FolderData arg0, String arg1)
   {
      return CmisUtils.emptyItemsIterator();
   }

}
